package com.feeyo.prophet.actions.flight;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.feeyo.llm.agent.engine.*;
import com.feeyo.llm.util.JsonUtil;
import com.feeyo.prophet.config.ParametersSingletonManager;
import com.feeyo.prophet.plugins.AnswerUtils;
import com.feeyo.prophet.plugins.ConstantUtils;
import com.feeyo.prophet.plugins.DbSearchActionUtils;
import com.feeyo.prophet.plugins.FlightScheduleActionUtils;
import com.feeyo.prophet.pojo.action.flight.AirportRank;
import com.feeyo.prophet.pojo.action.functioncall.FunctionCall;
import com.feeyo.prophet.pojo.action.functioncall.ItemsBean;
import com.feeyo.prophet.pojo.action.functioncall.flight.AirportRankProperties;
import com.feeyo.prophet.pojo.action.functioncall.flight.common.BasicTypeDefaultList;
import com.feeyo.prophet.pojo.action.rank.AirportRankResp;
import com.google.common.collect.Lists;
import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.HttpStatus;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 1.今年1月份限定级别是 限定 机场吞吐量排名
 * 2.今年1月份 时刻协调机场吞吐量排名
 * 3.今年1月份千万级以上机场排名
 * 4.今年1月份全国范围机场排名
 * 5.今年1月份合肥新桥机场排名
 *
 * @author tig
 */
@Slf4j
public class AirportRankAction implements Action {

    private final String host;
    private final Integer port;

    public AirportRankAction(String host, Integer port) {
        this.host = host;
        this.port = port;
    }

    @Override
    public String human() {
        return "机场排名查询";
    }

    @Override
    public String name() {
        return "AirportRankAction";
    }

    @Override
    public String eg() {
        return """
                Question: 我想查询2023年时刻协调机场排名。
                Thought: 我应该使用AirportRankAction搜索工具来查找机场分级排名, 时刻协调机场排名, 千万级以上机场排名, 千万级以下机场排名，全国机场排序的信息。
                Action: AirportRankAction
                Action Input: {"level":"0", "count":"10", "startDate":"2023-01-01", "endDate":"2023-12-31", "timeAirport": true}
                """;
    }

    @Override
    public String description() {
        return """
                机场排名查询，用于机场吞吐量查询、时刻协调机场排名查询、千万级以上机场排名查询、千万级以下机场排名查询、全国范围机场排名查询、指定机场查询其排名信息
                """;
    }

    @Override
    public String parameters() {
        String parameters = ParametersSingletonManager.getParameters("AirportRankAction", () -> {
            FunctionCall functionCall = new FunctionCall();
            functionCall.setType("object");
            functionCall.setRequired(Lists.newArrayList("fields"));
            AirportRankProperties properties = new AirportRankProperties();
            properties.setLevel(new BasicTypeDefaultList("string", "分配策略（也叫 限定级别），枚举值: '0：不限定, 1：限定, 2：限定非'", Lists.newArrayList("0", "1", "2")));
            properties.setCount(new BasicTypeDefaultList("number", "总数，例如: 10", 10, null));
            properties.setStartDate(new BasicTypeDefaultList("string", "开始日期，格式: yyyy-MM-dd，例如: '2019-01-01'"));
            properties.setEndDate(new BasicTypeDefaultList("string", "截止日期，格式: yyyy-MM-dd，例如: '2019-12-31'"));
            properties.setTimeAirport(new BasicTypeDefaultList("boolean", "是否为时刻协调机场，例如: false", false, null));
            properties.setOver10m(new BasicTypeDefaultList("boolean", "是否为千万级以上机场，例如: false", false, null));
            properties.setUnder10m(new BasicTypeDefaultList("boolean", "是否为千万级以下机场，例如: false", false, null));
            properties.setDomesticAirport(new BasicTypeDefaultList("boolean", "是否为全国机场，例如: false", false, null));
            properties.setAirports(new BasicTypeDefaultList("string", "机场IATA三字码，例如: 'PEK'"));
            properties.setCategory(new BasicTypeDefaultList("array", "机场类别数组，例如: ['国内']", new ItemsBean("string", Lists.newArrayList("国内", "国际"), null, null), new ArrayList<>()));
            List<ResultScheme.ResultAttach> resultAttachList = result().getResultAttachList();
            List<String> fieldsList = resultAttachList.stream().map(ResultScheme.ResultAttach::getName).toList();
            properties.setFields(new BasicTypeDefaultList("array", "返回结果字段集合(根据需要多选), 各个字段含义解释如下: " + JsonUtil.marshalToString(resultAttachList)
                    + "。可选填字段如下: " + String.join(", ", fieldsList) + ",你需要尽量全面的选出适用字段。可以选多，不要选少。", new ItemsBean("string"), 1));
            functionCall.setProperties(properties);
            return functionCall.toString();
        });
        return parameters;
    }

    @Override
    public ParameterScheme defaultParameters() {
        Map<String, ParameterScheme.Property> properties = new HashMap<>();
        ParameterScheme.Property levelProperty = new ParameterScheme.Property();
        levelProperty.setType("string");
        levelProperty.setDescription("限定级别，枚举值: '0: 不限定, 1: 限定, 2: 限定非'");
        levelProperty.setEnums(List.of("0", "1", "2"));
        properties.put("level", levelProperty);
        ParameterScheme.Property countProperty = new ParameterScheme.Property();
        countProperty.setType("number");
        countProperty.setDescription("总数，例如: 10");
        countProperty.setDefaults(10);
        properties.put("count", countProperty);
        ParameterScheme.Property startDateProperty = new ParameterScheme.Property();
        startDateProperty.setType("string");
        startDateProperty.setDescription("开始日期，格式: yyyy-MM-dd，例如: '2019-01-01'");
        startDateProperty.setExample("2019-01-01");
        properties.put("startDate", startDateProperty);
        ParameterScheme.Property endDateProperty = new ParameterScheme.Property();
        endDateProperty.setType("string");
        endDateProperty.setDescription("截止日期，格式: yyyy-MM-dd，例如: '2019-12-31'");
        endDateProperty.setExample("2019-12-31");
        properties.put("endDate", endDateProperty);
        ParameterScheme.Property timeAirportProperty = new ParameterScheme.Property();
        timeAirportProperty.setType("boolean");
        timeAirportProperty.setDescription("是否为时刻协调机场，例如: false");
        timeAirportProperty.setDefaults(false);
        properties.put("timeAirport", timeAirportProperty);
        ParameterScheme.Property over10mProperty = new ParameterScheme.Property();
        over10mProperty.setType("boolean");
        over10mProperty.setDescription("是否为千万级以上机场，例如: false");
        over10mProperty.setDefaults(false);
        properties.put("over10m", over10mProperty);
        ParameterScheme.Property under10mProperty = new ParameterScheme.Property();
        under10mProperty.setType("boolean");
        under10mProperty.setDescription("是否为千万级以下机场，例如: false");
        under10mProperty.setDefaults(false);
        properties.put("under10m", under10mProperty);
        ParameterScheme.Property domesticAirportProperty = new ParameterScheme.Property();
        domesticAirportProperty.setType("boolean");
        domesticAirportProperty.setDescription("是否为全国机场，例如: false");
        domesticAirportProperty.setDefaults(false);
        properties.put("domesticAirport", domesticAirportProperty);
        ParameterScheme.Property airportsProperty = new ParameterScheme.Property();
        airportsProperty.setType("array");
        airportsProperty.setDescription("机场IATA三字码，例如: 'PEK'");
        ParameterScheme.Property.Items airportsItems = new ParameterScheme.Property.Items();
        airportsItems.setType("string");
        airportsProperty.setItems(airportsItems);
        properties.put("airports", airportsProperty);
        ParameterScheme.Property categoryProperty = new ParameterScheme.Property();
        categoryProperty.setType("array");
        categoryProperty.setDescription("机场类别数组，例如: ['国内']");
        ParameterScheme.Property.Items categoryItems = new ParameterScheme.Property.Items();
        categoryItems.setType("string");
        categoryItems.setEnums(List.of("国内", "国际"));
        categoryProperty.setItems(categoryItems);
        properties.put("category", categoryProperty);
        List<ResultScheme.ResultAttach> resultAttachList = result().getResultAttachList();
        List<String> fieldsList = resultAttachList.stream().map(ResultScheme.ResultAttach::getName).toList();
        ParameterScheme.Property fieldsProperty = new ParameterScheme.Property();
        fieldsProperty.setType("array");
        fieldsProperty.setDescription("返回结果字段集合(根据需要多选), 字段含义: " + JsonUtil.marshalToString(resultAttachList)
                + "，可选字段: " + String.join(", ", fieldsList));
        ParameterScheme.Property.Items fieldsItems = new ParameterScheme.Property.Items();
        fieldsItems.setType("string");
        fieldsProperty.setItems(fieldsItems);
        properties.put("fields", fieldsProperty);
        ParameterScheme parameterScheme = new ParameterScheme("object", List.of("fields"), properties);
        return parameterScheme;
    }

    @Override
    public ResultScheme result() {
        ResultScheme resultScheme = new ResultScheme();
        List<ResultScheme.ResultAttach> resultAttachList = Lists.newArrayList();
        resultAttachList.add(new ResultScheme.ResultAttach("airport", "string", "机场IATA三字码"));
        resultAttachList.add(new ResultScheme.ResultAttach("rank", "integer", "排名"));
        resultAttachList.add(new ResultScheme.ResultAttach("throughput", "long", "吞吐量"));
        resultScheme.setResultAttachList(resultAttachList);
        return resultScheme;
    }

    @Override
    public ActionResult run(String question, String input, ExecuteOptions options) throws Exception {
        try {
            AirportRank req = JSON.parseObject(input, AirportRank.class);
            if (req.getOtherParam() != null && !req.getOtherParam().isEmpty()) {
                return new ActionResult(HttpStatus.BAD_REQUEST.value(), "发现暂时不支持的查询条件: " + req.getOtherParam());
            }
            String level = req.getLevel();
            Boolean timeAirport = req.getTimeAirport();
            Boolean airportsOver10m = req.getOver10m();
            Boolean airportsUnder10m = req.getUnder10m();
            Boolean domesticAirport = req.getDomesticAirport();
            String startDate = req.getStartDate();
            String endDate = req.getEndDate();
            String airport = req.getAirports();
            String category = req.getCategory();
            ResultScheme resultScheme = FlightScheduleActionUtils.resultScheme(req.getFields(), result());
            //对数量限定
            int count = ConstantUtils.COMMON_INT_TEN;
            if (req.getCount() != null && ConstantUtils.COMMON_INT_ZERO != req.getCount()) {
                count = req.getCount();
            }
            if (count > ConstantUtils.COMMON_INT_THIRTY) {
                count = ConstantUtils.COMMON_INT_THIRTY;
            }
            if (StringUtils.isNotEmpty(airport) && StringUtils.isEmpty(category) && (StringUtils.isEmpty(startDate) || StringUtils.isEmpty(endDate))) {
                ActionResult actionResult = new ActionResult(HttpStatus.BAD_REQUEST.value(), "指定机场排名查询, 必须给出查询时间范围, 国内/国际的区域范围");
                actionResult.setReturnDirect(true);
                return actionResult;
            }
            // 机场吞吐量查询
            if (StringUtils.isNotEmpty(level) && (airportsOver10m == null || !airportsOver10m)
                    && (airportsUnder10m == null || !airportsUnder10m) && (timeAirport == null || !timeAirport)
                    && (domesticAirport == null || !domesticAirport) && StringUtils.isEmpty(airport)) {
                return calAirportLevelRank(input, count, level, resultScheme);
            }
            // 时刻协调机场排名
            if (timeAirport != null && timeAirport) {
                return calTimeAirportRank(input, startDate, endDate, count, resultScheme);
            }
            // 千万级以上机场排名
            if (airportsOver10m != null && airportsOver10m) {
                return calAirportOver10mRank(input, startDate, endDate, count, resultScheme);
            }
            // 千万级以下机场排名
            if (airportsUnder10m != null && airportsUnder10m) {
                return calAirportsUnder10m(input, startDate, endDate, count, resultScheme);
            }
            //全国范围机场排名
            if (domesticAirport != null && domesticAirport) {
                return calDomesticAirport(input, startDate, endDate, count, resultScheme);
            }
            //指定机场排名
            if (StringUtils.isNotEmpty(airport)) {
                return calSpecifyAirport(input, startDate, endDate, airport, category, resultScheme);
            }
            return new ActionResult(HttpStatus.BAD_REQUEST.value(), "暂不支持该条件查询");
        } catch (Exception e) {
            log.error("AirportThroughputRankAction error", e);
            return new ActionResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), "AirportThroughputRankAction error");
        }
    }

    private ActionResult calSpecifyAirport(String input, String startDate, String endDate, String airport, String category, ResultScheme resultScheme) throws Exception {
        if (StringUtils.isEmpty(startDate) || StringUtils.isEmpty(endDate)) {
            return new ActionResult(HttpStatus.BAD_REQUEST.value(), "指定机场排名,开始日期、截止日期必须存在");
        }
        String respTxt = DbSearchActionUtils.specifyAirportRank(host, port, startDate, endDate, airport, category);
        if (StringUtils.isEmpty(respTxt)) {
            return new ActionResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), "指定机场排名查询发生错误，请稍后再用");
        }
        JSONObject json = JSONObject.parseObject(respTxt);
        JSONObject data = json.getJSONObject("data");
        if (null == data) {
            return new ActionResult(HttpStatus.BAD_REQUEST.value(), "未查询到指定机场数据");
        }
        List<AirportRankResp> flightScheduleResps = Lists.newArrayList();
        JSONArray rowArray = data.getJSONArray("rows");
        if (!rowArray.isEmpty()) {
            for (Object row : rowArray) {
                flightScheduleResps.add(JSON.parseObject(String.valueOf(row), AirportRankResp.class));
            }
        }
        return AnswerUtils.answer(name(), input, flightScheduleResps, resultScheme);
    }

    /**
     * 时刻协调机场吞吐量排序,
     *
     * @param input 入参
     * @param count output count
     * @param level 0:不限定、1:限定、2:限定非
     */
    private ActionResult calAirportLevelRank(String input, int count, String level, ResultScheme resultScheme) {
        int airPort = Integer.parseInt(level);
        String resp = DbSearchActionUtils.getAirports10m(host, port, airPort, count);
        if (StringUtils.isEmpty(resp)) {
            return new ActionResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), "机场吞吐量查询发生错误，请稍后再用");
        }
        log.info("机场数据查询结果：{}", resp);
        JSONObject json = JSONObject.parseObject(resp);
        JSONObject data = json.getJSONObject("data");
        if (null == data) {
            return new ActionResult(HttpStatus.BAD_REQUEST.value(), "未查询到机场数据");
        }
        List<AirportRankResp> flightScheduleResps = Lists.newArrayList();
        JSONArray rowArray = data.getJSONArray("rows");
        if (!rowArray.isEmpty()) {
            for (Object row : rowArray) {
                flightScheduleResps.add(JSON.parseObject(String.valueOf(row), AirportRankResp.class));
            }
        }
        return AnswerUtils.answer(name(), input, flightScheduleResps, resultScheme);
    }

    @NotNull
    private ActionResult calDomesticAirport(String input, String startDate, String endDate, int count, ResultScheme resultScheme) throws Exception {
        if (StringUtils.isEmpty(startDate) || StringUtils.isEmpty(endDate)) {
            return new ActionResult(HttpStatus.BAD_REQUEST.value(), "全国范围机场排名,开始日期、截止日期必须存在");
        }
        String respTxt = DbSearchActionUtils.domesticAirportsRank(host, port, startDate, endDate, count);
        if (StringUtils.isEmpty(respTxt)) {
            return new ActionResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), "全国范围机场排名查询发生错误，请稍后再用");
        }
        log.info("全国范围机场机场查询结果：{}", respTxt);
        JSONObject json = JSONObject.parseObject(respTxt);
        JSONObject data = json.getJSONObject("data");
        if (null == data) {
            return new ActionResult(HttpStatus.BAD_REQUEST.value(), "未查询到全国范围机场数据");
        }
        List<AirportRankResp> flightScheduleResps = Lists.newArrayList();
        JSONArray rowArray = data.getJSONArray("rows");
        if (!rowArray.isEmpty()) {
            for (Object row : rowArray) {
                flightScheduleResps.add(JSON.parseObject(String.valueOf(row), AirportRankResp.class));
            }
        }
        return AnswerUtils.answer(name(), input, flightScheduleResps, resultScheme);
    }

    @NotNull
    private ActionResult calAirportsUnder10m(String input, String startDate, String endDate, int count, ResultScheme resultScheme) {
        if (StringUtils.isEmpty(startDate) || StringUtils.isEmpty(endDate)) {
            return new ActionResult(HttpStatus.BAD_REQUEST.value(), "千万级以下机场排名,开始日期、截止日期必须存在");
        }
        String respTxt = DbSearchActionUtils.getAirportsRankUnder10m(host, port, startDate, endDate, count);
        if (StringUtils.isEmpty(respTxt)) {
            return new ActionResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), "千万级以下机场排名查询发生错误，请稍后再用");
        }
        log.info("千万级以下机场查询结果：{}", respTxt);
        JSONObject json = JSONObject.parseObject(respTxt);
        JSONObject data = json.getJSONObject("data");
        if (null == data) {
            return new ActionResult(HttpStatus.BAD_REQUEST.value(), "未查询到千万级以下机场排名");
        }
        List<AirportRankResp> flightScheduleResps = Lists.newArrayList();
        JSONArray rowArray = data.getJSONArray("rows");
        if (!rowArray.isEmpty()) {
            for (Object row : rowArray) {
                flightScheduleResps.add(JSON.parseObject(String.valueOf(row), AirportRankResp.class));
            }
        }
        return AnswerUtils.answer(name(), input, flightScheduleResps, resultScheme);
    }

    @NotNull
    private ActionResult calTimeAirportRank(String input, String startDate, String endDate, int count, ResultScheme resultScheme) {
        if (StringUtils.isEmpty(startDate) || StringUtils.isEmpty(endDate)) {
            return new ActionResult(HttpStatus.BAD_REQUEST.value(), "时刻协调机场排名查询,开始日期、截止日期必须存在");
        }
        String respTxt = DbSearchActionUtils.getTimeAirportsRank(host, port, startDate, endDate, count);
        if (StringUtils.isEmpty(respTxt)) {
            return new ActionResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), "时刻协调机场排名查询发生错误，请稍后再用");
        }
        log.info("时刻协调机场排序查询结果：{}", respTxt);
        JSONObject json = JSONObject.parseObject(respTxt);
        JSONObject data = json.getJSONObject("data");
        if (null == data) {
            return new ActionResult(HttpStatus.BAD_REQUEST.value(), "未查询到时刻协调机场数据");
        }
        List<AirportRankResp> flightScheduleResps = Lists.newArrayList();
        JSONArray rowArray = data.getJSONArray("rows");
        if (!rowArray.isEmpty()) {
            for (Object row : rowArray) {
                flightScheduleResps.add(JSON.parseObject(String.valueOf(row), AirportRankResp.class));
            }
        }
        return AnswerUtils.answer(name(), input, flightScheduleResps, resultScheme);
    }

    @NotNull
    private ActionResult calAirportOver10mRank(String input, String startDate, String endDate, int count, ResultScheme resultScheme) {
        if (StringUtils.isEmpty(startDate) || StringUtils.isEmpty(endDate)) {
            return new ActionResult(HttpStatus.BAD_REQUEST.value(), "千万级以上机场排名,开始日期、截止日期必须存在");
        }
        String respTxt = DbSearchActionUtils.getAirportsRankOver10m(host, port, startDate, endDate, count);
        if (StringUtils.isEmpty(respTxt)) {
            return new ActionResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), "千万级以上机场排名查询发生错误，请稍后再用");
        }
        log.info("千万级以上机场数据结果：{}", respTxt);
        JSONObject json = JSONObject.parseObject(respTxt);
        JSONObject data = json.getJSONObject("data");
        if (null == data) {
            return new ActionResult(HttpStatus.BAD_REQUEST.value(), "未查询到千万级以上机场数据");
        }
        List<AirportRankResp> flightScheduleResps = Lists.newArrayList();
        JSONArray rowArray = data.getJSONArray("rows");
        if (!rowArray.isEmpty()) {
            for (Object row : rowArray) {
                flightScheduleResps.add(JSON.parseObject(String.valueOf(row), AirportRankResp.class));
            }
        }
        return AnswerUtils.answer(name(), input, flightScheduleResps, resultScheme);
    }
}